﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CloudShot.Utils.Converters;
using Newtonsoft.Json;

namespace CloudShot.Entities
{
  [Flags]
  public enum KeyModifiers
  {
    None = 0,
    Alt = 1,
    Ctrl = 2,
    Shift = 4,
    Win = 8,
  }

  [JsonConverter(typeof(HotkeyJsonConverter))]
  public struct Hotkey : IEquatable<Hotkey>
  {
    private static readonly char[] _separators = { '+', ',', ' ', '\t' };

    public static readonly Hotkey Empty = new Hotkey();
    
    public bool IsEmpty
    {
      get { return this == Empty; }
    }

    public KeyModifiers Modifiers { get; set; }

    public Keys Key { get; set; }

    public Hotkey(Keys key)
      : this()
    {
      Key = key;
    }

    public Hotkey(Keys key, KeyModifiers modifiers)
      : this()
    {
      Key = key;
      Modifiers = modifiers;
    }

    public static bool TryParse(string value, out Hotkey hotkey)
    {
      hotkey = Hotkey.Empty;

      if (string.IsNullOrEmpty(value))
        return false;

      value = NormalizeHotkey(value);

      var keys = value.Split(_separators, StringSplitOptions.RemoveEmptyEntries);

      var modifiers = KeyModifiers.None;
      var key = Keys.None;

      foreach (string keyValue in keys)
      {
        KeyModifiers modifier;
        if (Enum.TryParse(keyValue, true, out modifier))
        {
          modifiers |= modifier;
          continue;
        }

        if (!Enum.TryParse(keyValue, true, out key))
          return false;
      }

      hotkey = new Hotkey(key, modifiers);
      
      return !hotkey.IsEmpty;
    }

    public override string ToString()
    {
      if (IsEmpty)
        return Keys.None.ToString();

      var result = new List<string>();

      if (Modifiers != KeyModifiers.None)
        result.AddRange(Modifiers.ToString().Split(_separators, StringSplitOptions.RemoveEmptyEntries));

      if (Key != Keys.None)
        result.Add(Key.ToString());

      return NormalizeHotkey(string.Join(" + ", result));
    }

    private static string NormalizeHotkey(string value)
    {
      value = value.Replace(Keys.Control.ToString(), "Ctrl");
      value = value.Replace(Keys.Escape.ToString(), "Esc");
      value = value.Replace(Keys.LWin.ToString(), "Win");
      value = value.Replace(Keys.RWin.ToString(), "Win");
      value = value.Replace(Keys.Return.ToString(), "Enter");

      return value;
    }

    #region Equality Operations

    public override bool Equals(object obj)
    {
      if (obj is Hotkey)
        return Equals((Hotkey)obj);

      return false;
    }

    public bool Equals(Hotkey other)
    {
      return Modifiers == other.Modifiers && Key == other.Key;
    }

    public static bool operator ==(Hotkey left, Hotkey right)
    {
      return left.Equals(right);
    }

    public static bool operator !=(Hotkey left, Hotkey right)
    {
      return !left.Equals(right);
    }

    public override int GetHashCode()
    {
      unchecked
      {
        return ((int)Modifiers * 397) ^ (int)Key;
      }
    }

    #endregion
  }
}